fix(xiaohongshu/user): 登录墙报 AUTH_REQUIRED + 修 hydration 竞态#1945
Merged
Conversation
`xiaohongshu user` 在两种场景下都误报,下游无法区分:
## 场景 1:登录态失效被误报成 "Malformed user store" / EMPTY_RESULT
小红书 profile 页比 search 更吃登录态。会话失效 / 被风控降级时,访问
`/user/profile/<id>` 会 302 重定向到 `/login`(`__INITIAL_STATE__.user.loggedIn=false`,
页面挂登录二维码)。老逻辑读到登录页的空 user store → 抛 "Malformed Xiaohongshu user
snapshot: user store was not found",或等到 store 出现却 notes 全空 → EMPTY_RESULT。
下游(如 ml-scout)据此把**登录失效**误判成**解析失败 / 空号**:白等 rate-limit
cooldown、误锁平台。实测 2026-06-09:风控把 profile 浏览态降级 → 整批 seed 创作者全
302 到 /login,被报成 Malformed,排查了很久才定位是登录墙。
修复:USER_SNAPSHOT_JS 增加 `loginWall` 检测(pathname 落在 /login,或 loggedIn===false),
命中即抛 `AuthRequiredError`(code AUTH_REQUIRED,exit NOPERM)。语义正确,下游一眼可辨,
提示用户重登 xiaohongshu.com。
## 场景 2:hydration 竞态(间歇性 "user store was not found")
`__INITIAL_STATE__.user` 由 SSR / client bootstrap 异步注入;`page.goto` 后**立刻**
`page.evaluate` 会撞 hydration 窗口 → store/notes 尚未就绪。note.js(`page.wait({time:2+rand*3})`)
与 download.js(`page.wait({time:1+rand*2})`)早已用 goto 后等待规避,唯独 user.js 漏了
→ 慢加载必现、快加载侥幸过的间歇性失败(2026-06-09 复现、2026-05-20 亦有记录)。
修复:新增 `readUserSnapshotHydrated`——先快读一次(已就绪零额外延迟,保住快加载路径),
未拿到笔记**且非登录墙**就 wait 后重试至多 maxRetries 次。笔记是 `[tab[], ...]` 形态、首屏
可能晚于 store 填充,故用 `countFlatNotes`(展平后真实条数)作为就绪判据,而非 store 在即停。
登录墙命中立即停(再等无用);真·空号走满重试后由 EmptyResultError 正确收尾。
## 测试
- 新增 clis/xiaohongshu/user.test.js(14 例):countFlatNotes / isLoginWallSnapshot /
readUserSnapshotHydrated(快路径不 wait、慢加载重试、登录墙即停、空号走满重试)/
command.func(登录墙→AUTH_REQUIRED、笔记晚到→重试成功、空号→EMPTY_RESULT)
- 端到端:对真实登录失效会话,`opencli xiaohongshu user <id>` 现报 AUTH_REQUIRED(exit 77),
替代旧的误导性 Malformed/EMPTY
- xiaohongshu + rednote 全套 231 例通过(USER_SNAPSHOT_JS 被 rednote 复用,loginWall 为
additive 字段、无回归)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
xiaohongshu user在两种场景下都误报,下游(以及人工排查)无法区分到底是「登录失效」「解析失败」还是「真空号」。本 PR 把两类误报修成语义正确的结构化错误。改动集中在clis/xiaohongshu/user.js(+ 新增测试),USER_SNAPSHOT_JS被 rednote 复用,rednote 一并受益。场景 1:登录态失效被误报成 "Malformed user store" / EMPTY_RESULT
小红书 profile 页比 search 更吃登录态。会话失效 / 被风控降级时,访问
/user/profile/<id>会 302 重定向到/login(__INITIAL_STATE__.user.loggedIn=false,页面挂登录二维码)。旧逻辑读到登录页的空 user store → 抛Malformed Xiaohongshu user snapshot: user store was not found,或等到 store 出现却 notes 全空 →EMPTY_RESULT。下游据此把登录失效误判成解析失败 / 空号:白等 rate-limit cooldown、误锁平台。实测 2026-06-09:风控把 profile 浏览态降级 → 整批 seed 创作者全 302 到
/login,被报成 Malformed,排查很久才定位是登录墙。修复:
USER_SNAPSHOT_JS增加loginWall检测(pathname 落在/login,或loggedIn===false),命中即抛AuthRequiredError(codeAUTH_REQUIRED,exitNOPERM)。语义正确,下游一眼可辨,并提示用户重登 xiaohongshu.com。场景 2:hydration 竞态(间歇性 "user store was not found")
__INITIAL_STATE__.user由 SSR / client bootstrap 异步注入;page.goto后立刻page.evaluate会撞 hydration 窗口 → store / notes 尚未就绪。兄弟命令note.js(page.wait({time:2+rand*3}))与download.js(page.wait({time:1+rand*2}))早已用 goto 后等待规避,唯独user.js漏了 → 慢加载必现、快加载侥幸过的间歇性失败。修复:新增
readUserSnapshotHydrated—— 先快读一次(已就绪零额外延迟,保住快加载路径),未拿到笔记且非登录墙才 wait 后重试至多maxRetries次。笔记是[tab[], ...]形态、首屏可能晚于 store 填充,故用countFlatNotes(展平后真实条数)作就绪判据,而非 store 在即停。登录墙命中立即停(再等无用);真·空号走满重试后由EmptyResultError正确收尾。测试
clis/xiaohongshu/user.test.js(14 例):countFlatNotes/isLoginWallSnapshot/readUserSnapshotHydrated(快路径不 wait、慢加载重试、登录墙即停、空号走满重试)/command.func(登录墙→AUTH_REQUIRED、笔记晚到→重试成功、空号→EMPTY_RESULT)opencli xiaohongshu user <id>现报AUTH_REQUIRED(exit 77),替代旧的误导性 Malformed / EMPTYUSER_SNAPSHOT_JS被 rednote 复用,loginWall为 additive 字段、无回归);tsc --noEmit干净兼容性
AuthRequiredError(exit 77 / codeAUTH_REQUIRED),是新的、更准确的错误分类 —— 之前这种情况错误地表现为 Malformed / EMPTY_RESULTloginWall是 snapshot 的 additive 字段,不改既有输出结构